package task

import (
	"context"
	"sync"
	"time"

	"github.com/go-redis/redis/v8"
	"github.com/google/uuid"
	"go.uber.org/zap"
)

// TaskFunc 定义任务执行函数类型
type TaskFunc func(ctx context.Context) error

// ScheduledTask 单个定时任务的结构
type ScheduledTask struct {
	name        string        // 任务唯一名称
	interval    time.Duration // 执行间隔
	taskFunc    TaskFunc      // 任务执行函数
	redisClient *redis.Client // Redis客户端
	logger      *zap.Logger   // 日志实例
	stopChan    chan struct{} // 用于停止任务的通道
	isRunning   bool          // 任务是否在运行
	mu          sync.Mutex    // 保护isRunning的互斥锁
}

// TaskManager 任务管理器，用于管理多个定时任务
type TaskManager struct {
	tasks       map[string]*ScheduledTask // 存储所有任务
	redisClient *redis.Client             // Redis客户端
	logger      *zap.Logger               // 日志实例
	mu          sync.RWMutex              // 保护tasks的读写锁
}

// NewTaskManager 创建任务管理器
func NewTaskManager(redisClient *redis.Client, logger *zap.Logger) *TaskManager {
	return &TaskManager{
		tasks:       make(map[string]*ScheduledTask),
		redisClient: redisClient,
		logger:      logger,
	}
}

// AddTask 添加定时任务
func (m *TaskManager) AddTask(name string, interval time.Duration, taskFunc TaskFunc) {
	m.mu.Lock()
	defer m.mu.Unlock()

	// 如果任务已存在，先停止它
	if existingTask, exists := m.tasks[name]; exists {
		existingTask.Stop()
	}

	// 创建新任务
	task := &ScheduledTask{
		name:        name,
		interval:    interval,
		taskFunc:    taskFunc,
		redisClient: m.redisClient,
		logger:      m.logger.With(zap.String("task_name", name)),
		stopChan:    make(chan struct{}),
	}

	m.tasks[name] = task
}

// StartAll 启动所有任务
func (m *TaskManager) StartAll() {
	m.mu.RLock()
	defer m.mu.RUnlock()

	for _, task := range m.tasks {
		go task.Start()
	}

	m.logger.Info("所有定时任务已启动", zap.Int("task_count", len(m.tasks)))
}

// StopAll 停止所有任务
func (m *TaskManager) StopAll() {
	m.mu.RLock()
	defer m.mu.RUnlock()

	for _, task := range m.tasks {
		task.Stop()
	}

	m.logger.Info("所有定时任务已停止")
}

// Start 启动单个任务
func (t *ScheduledTask) Start() {
	t.mu.Lock()
	if t.isRunning {
		t.mu.Unlock()
		t.logger.Warn("任务已在运行中")
		return
	}
	t.isRunning = true
	t.mu.Unlock()

	t.logger.Info("任务启动", zap.Duration("interval", t.interval))

	// 立即执行一次，然后按间隔执行
	t.runOnce()

	ticker := time.NewTicker(t.interval)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			t.runOnce()
		case <-t.stopChan:
			t.mu.Lock()
			t.isRunning = false
			t.mu.Unlock()
			t.logger.Info("任务已停止")
			return
		}
	}
}

// Stop 停止单个任务
func (t *ScheduledTask) Stop() {
	t.mu.Lock()
	defer t.mu.Unlock()

	if !t.isRunning {
		return
	}

	close(t.stopChan)
	t.isRunning = false
}

// runOnce 执行一次任务（带分布式锁）
func (t *ScheduledTask) runOnce() {
	// 生成任务执行ID
	execID := generateExecID()
	logger := t.logger.With(zap.String("exec_id", execID))
	ctx := context.WithValue(context.Background(), "exec_id", execID)

	logger.Info("准备执行任务,taskName=" + t.name)

	// 尝试获取分布式锁
	lockKey := getLockKey(t.name)
	lockExpire := 5 * time.Minute // 锁过期时间，根据任务耗时调整

	// 使用Redis的SET NX获取锁
	gotLock, err := t.redisClient.SetNX(ctx, lockKey, execID, lockExpire).Result()
	if err != nil {
		logger.Error("获取分布式锁失败", zap.Error(err))
		return
	}

	// 未获取到锁，说明其他节点正在执行
	if !gotLock {
		logger.Debug("未获取到分布式锁，跳过任务执行")
		return
	}

	// 确保释放锁
	defer func() {
		// 使用Lua脚本确保原子性释放锁（防止误删其他节点的锁）
		script := `
			if redis.call('get', KEYS[1]) == ARGV[1] then
				return redis.call('del', KEYS[1])
			else
				return 0
			end
		`
		_, err := t.redisClient.Eval(ctx, script, []string{lockKey}, execID).Result()
		if err != nil {
			logger.Warn("释放分布式锁失败", zap.Error(err))
		} else {
			logger.Debug("分布式锁已释放")
		}
	}()

	// 执行任务
	logger.Info("开始执行任务,taskName=" + t.name)
	startTime := time.Now()

	if err := t.taskFunc(ctx); err != nil {
		logger.Error("任务执行失败", zap.Error(err), zap.Duration("duration", time.Since(startTime)))
		return
	}

	logger.Info("任务执行成功,taskName="+t.name, zap.Duration("duration", time.Since(startTime)))
}

// 生成任务执行ID
func generateExecID() string {
	return uuid.NewString()
}

// 获取锁的Redis键名
func getLockKey(taskName string) string {
	return "scheduled_task:lock:" + taskName
}
